package cn.hn.java.summer.db;


import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.context.ContextLoader;

import cn.hn.java.summer.context.ThreadContextManage;
import cn.hn.java.summer.db.multiple.IDataSourceMark;
import cn.hn.java.summer.db.multiple.MultipleDataSource;
import cn.hn.java.summer.db.paging.IPaging;
import cn.hn.java.summer.db.paging.Page;
import cn.hn.java.summer.exception.SummerException;
import cn.hn.java.summer.utils.ReflectUtils;


/**
 * 其中用实现IMultipleIndex接口的Index来标识使用哪个数据源
 * @author sjg
 * @version 1.0.1 2013-10-27
 * 
 * @param <Index>
 */
@Component
public class BaseDao<Index extends IDataSourceMark> implements IDbOperator {
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	
	/**
	 * 多数据源
	 */
	@Autowired
	private MultipleDataSource multipleDataSource;
	
	/**
	 * 当前使用的JdbcTemplate
	 */
	protected DbOperator jdbcTemplate;
	
	/**
	 * 取jdbcTemplate，调用getDataSource获取数据源
	 * @return
	 */
	public DbOperator getDbOperator(){
		//由子类指定数据源
		DataSource ds=this.getDataSource();
		if(ds!=null){
			jdbcTemplate.setDataSource(this.getDataSource());
		}
		
		return jdbcTemplate;
	}
	
	/**
	 * 初始化
	 * @throws SummerException
	 */
	@PostConstruct
	private void init() throws SummerException {
		//取Index的类型
		Class<?> cls= ReflectUtils.getClassGenericType(getClass(), 0);
		if(jdbcTemplate==null){
			try {
				//根据Index的类型取对应的jdbcTemplate
				jdbcTemplate=multipleDataSource.get(cls.newInstance());
			} catch (Exception ex) {
				logger.error("设置jdbcTemplate出错",ex);
				throw new SummerException("初始化数据源失败",ex);
			}
		}
	}
	
	/**
	 * 动态取数据源，每次执行数据库操作时调用。
	 * 请谨慎实现该方法，确保在需要创建DataSource时才创建新的
	 * @throws SummerException
	 */
	public DataSource getDataSource(){
		return null;
	}
	
	public void setDataSource(DataSource ds){	
	}
	
	/**
	 * 执行任意sql语句或id
	 */
	public void execute(String sql) {
		getDbOperator().execute(sql);
	}

	/**
	 * 取分页参数信息
	 * @return 分页参数
	 */
	private Page getPageInfo(Object... args){
		//取分页信息
		Page page= ThreadContextManage.getPagingInfo();
		//上下文中未取到
		if(page==null){
			//从参数中取
			for(Object arg : args){
				if(arg instanceof IPaging){
					IPaging ipaging=(IPaging)arg;
					//分页设置正确
					if(ipaging.getPageSize()>0 && ipaging.getPage()>0){
						page=new Page(ipaging.getPageSize(),ipaging.getPage());
						break;
					}
				}
			}
		}
		return page;
	}

	/**
	 * 取单个值
	 * @param type 返回值类型
	 * @param sql sql语句或id
	 * @param args 传入参数
	 */
	public <T> T get(String sql, Class<T> type, Object... args) {
		return getDbOperator().get(sql, type, args);
	}

	/**
	 * 取列表
	 * @param elementType 返回值类型
	 * @param sql sql语句或id
	 * @param args 传入参数
	 */
	public <T> List<T> list(String sql, Class<T> elementType, Object... args){
		Page page=getPageInfo(args);
		if(page==null){
			return getDbOperator().list(sql, elementType, args);
		}else{
			return getDbOperator().list(sql, elementType,page.getPageSize(),page.getPage(),args);
		}
	}

	/**
	 * 分页取列表,建议只在非web程序中使用,web程序中会自动进行分页
	 * @param <T>
	 * @param sql
	 * @param elementType
	 * @param pageSize
	 * @param page
	 * @param args
	 * @return
	 * @throws SummerException
	 */
	public <T> List<T> page(String sql, Class<T> elementType, int pageSize, int page, Object... args) throws SummerException {
		return getDbOperator().list(sql, elementType,pageSize,page,args);
	}
	/**
	 * 分页取列表,建议只在非web程序中使用,web程序中会自动进行分页
	 * @param <T>
	 * @param sql
	 * @param orderSql 封装好的排序语句，如： order by a desc,b asc 用来替换sql语句里的关键字#OrderBy#
	 * @param elementType
	 * @param pageSize
	 * @param page
	 * @param args
	 * @return
	 * @throws SummerException
	 */
	public <T> List<T> pageByOrder(String sql,String orderSql, Class<T> elementType, int pageSize, int page, Object... args) throws SummerException {
		if(orderSql == null)orderSql ="";
		return getDbOperator().listByOrder(sql, orderSql,elementType,pageSize,page,args);
	}

	/**
	 * 批量取列表(暂不支持分页)
	 * 如：要同时跨多个表查询时，sql除表名外其它都一样，这样就可以把多个查询合并到一起查询，提供批量的查询参数args
	 * @param sql sql语句或id
	 * @param elementType 返回值类型
	 * @param args 批量参数
	 */
	public <T> List<T> list(String sql, Class<T> elementType, List<Object[]> args){
		return getDbOperator().list(sql, elementType, args);
	}

	/**
	 * 强制取所有数据不分页的方法
	 * @param <T>
	 * @param sql
	 * @param elementType
	 * @param args
	 * @return
	 */
	public <T> List<T> all(String sql,Class<T> elementType, Object...args){
		return getDbOperator().list(sql, elementType, args);
	}

	/**
	 * 修改、添加、删除操作
	 * @param sql sql语句或id
	 * @param args 传入参数
	 */
	public int update(String sql, Object... args) {
		return getDbOperator().update(sql, args);
	}

	/**
	 * 	批量更新操作
	 * 	在调用本方法的方法上，加上@Transactional。将批处理作为一个事务来处理，可以极大提高效率。亲测可行
	 *        继承本类再调用本方法时，由于是同类方法调用，切面拦截不起效果，事务不生效。只能在继承类的方法上再加上@Transactional
	 * @param sql
	 * @param batchArgs
	 * @return
	 * @throws SummerException
	 * 
	 * </p>注意：
	 * 如果调用类是直接 extends BaseDao<DataSource1>
	 * 则在调用类里面直接调用本方法，是内内部方法调用，事务不会生效。那么批处理添加的时候，对于已经添加成功的数据本方法是不会做回滚操作的
	 * </p>如：表id为主键
	 * 依次添加id为：1,2,3,1,5的数据时，只会添加成功1,2,3，再次添加1的时候会报主键冲突，后面的数据都会停止添加了，已添加的数据也不会回滚
	 * </p>想要数据回滚，一条数据也不添加，使用下面的batchUpdateRollbackFor方法
	 */
	@SuppressWarnings("rawtypes")
	@Transactional(rollbackFor = {RuntimeException.class,Exception.class})
	public <T> int[] batchUpdate(String sql, List<T> batchArgs,IBatchArgMapper mapper) throws SummerException {
		return getDbOperator().batchUpdate(sql, batchArgs,mapper);
	}
	
	/**
	 * 批量更新操作，使用手动提交事务的方式，确保数据一致性
	 * 更新时任何一条数据出现异常，所有数据全部回滚
	 * @param <T>
	 * @param sql
	 * @param batchArgs
	 * @param mapper
	 * @throws SummerException
	 * </p>注意：
	 * <p>1、手动设置事务时，必须确保连接是同一个连接，即所有操作使用Connection connection = null里的connection
	 * <p>2、connection.setAutoCommit(false); 后需要在finally里设置回true
	 */
	public <T> void batchUpdateRollbackFor(String sql, List<T> batchArgs,IBatchArgMapper mapper) throws Exception {
		// spring无法处理thread的事务，声明式事务无效
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
		PlatformTransactionManager txManager = ContextLoader.getCurrentWebApplicationContext().getBean(PlatformTransactionManager.class);
		TransactionStatus status = txManager.getTransaction(def);
		try {
			getDbOperator().batchUpdate(sql, batchArgs,mapper);
			txManager.commit(status); // 提交事务
		} catch (Exception e) {
			logger.warn("手动提交事务时，批处理异常，操作批处理开始回滚!");
			txManager.rollback(status); // 回滚事务
			logger.info("手动提交事务时，批处理异常，操作批处理回滚成功!");
		}
		
	}
	public <T> void batchUpdateRollbackFor1(String sql, List<T> batchArgs,IBatchArgMapper mapper) throws Exception {
		Connection connection = null;
		try { 
			connection = jdbcTemplate.getDataSource().getConnection();
			connection.setAutoCommit(false);
			getDbOperator().batchUpdate(sql, batchArgs,mapper);
			//手动提交事务
			connection.commit();
		} catch (Exception e) {
			//logger.error("手动提交事务时，批处理异常，开始回滚！",e);
			// 回滚事务
			if(connection != null) {
				try {
					connection.rollback();
					logger.info("手动提交事务时，批处理异常，操作批处理回滚成功!");
				} catch (SQLException rollbackEx) {
					logger.error("手动提交事务时，批处理异常，操作批处理回滚异常!",rollbackEx);
				}
			}
			//捕获到的异常需要抛出去，不能自己就出列了
			throw e;
		}finally {
			// 关闭连接
			if (connection != null) {
				try {
					connection.setAutoCommit(true);
					connection.close();
				} catch (SQLException closeEx) {
					logger.error("手动提交事务后，还原为自动提交事务时异常，关闭连接异常!",closeEx);
				}
			}
		}
	}

	/**
	 * 生成一个条件项
	 * @return
	 */
	public ConditionEntity ce(){
		return new ConditionEntity();
	}


	//===================================不用传sql的部分==================================

	public OrderBy order(){
		return new OrderBy();
	}

	/**
	 * 取单表列表
	 *
	 * @param elementType 实体类
	 * @param args        查询参数
	 * @return 实体列表
	 */
	@Override
	public <T> List<T> list(Class<T> elementType, Object... args) {
		Page page=getPageInfo(args);
		if(page==null){
			return getDbOperator().list(elementType, args);
		}else{
			return getDbOperator().list(elementType,page.getPageSize(),page.getPage(),null,args);
		}
	}


	/**
	 * 取单表列表
	 *
	 * @param elementType 实体类
	 * @param orderBy     排序
	 * @param args        查询参数
	 * @return 实体列表
	 */
	@Override
	public <T> List<T> list(Class<T> elementType, OrderBy orderBy, Object... args) {
		Page page=getPageInfo(args);
		if(page==null){
			return getDbOperator().list(elementType,orderBy,args);
		}else{
			return getDbOperator().list(elementType,page.getPageSize(),page.getPage(),orderBy,args);
		}
	}

	/**
	 * 单表-强制取所有数据不分页的方法
	 *
	 * @param elementType 实体类
	 * @param args        查询参数
	 * @return 实体列表
	 */
	@Override
	public <T> List<T> all(Class<T> elementType, Object... args) {
		return getDbOperator().all(elementType,args);
	}

	/**
	 * 单表-查询单个对象值
	 *
	 * @param type 实体类
	 * @param args 查询参数
	 * @return 实体列表
	 */
	@Override
	public <T> T get(Class<T> type, Object... args) {
		return getDbOperator().get(type,args);
	}

	/**
	 * 按主键更新
	 *
	 * @param bean 实体
	 * @return 更新记录数
	 */
	@Override
	public <T> int update(T bean) {
		return getDbOperator().update(bean);
	}

	/**
	 * 按条件更新
	 *
	 * @param bean      实体
	 * @param condition 条件
	 * @return 更新记录数
	 */
	@Override
	public <T extends Object> int update(T bean, ConditionEntity condition) {
		if(bean instanceof String){
			return getDbOperator().update(bean.toString(), new Object[]{condition});
		}
		return getDbOperator().update(bean,condition);
	}

	/**
	 * 插入
	 *
	 * @param bean 实体
	 * @return 插入记录数
	 */
	@Override
	public <T> int insert(T bean) {
		return getDbOperator().insert(bean);
	}

	/**
	 * 按主键删除
	 *
	 * @param bean 实体
	 * @return 删除记录数
	 */
	@Override
	public <T> int delete(T bean) {
		return getDbOperator().delete(bean);
	}

	/**
	 * 按指定条件删除
	 *
	 * @param type         实体类型类
	 * @param condition 条件键值
	 * @return 删除记录数
	 */
	@Override
	public <T> int delete(Class<T> type, ConditionEntity condition) {
		return getDbOperator().delete(type,condition);
	}
}
